<?php
/**
 * GeryEngine
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this release in the file LICENSE
 *
 * @copyright  Copyright (c) 2007 Arjen Brouwer
 * @license    New BSD License
 * @author     Arjen Brouwer
 */

define('LOG_LEVEL_FATAL', 		0x01);
define('LOG_LEVEL_CRITICAL', 	0x02);
define('LOG_LEVEL_ERROR',		0x04);
define('LOG_LEVEL_WARNING', 	0x08);
define('LOG_LEVEL_INFO',		0x10);
define('LOG_LEVEL_DEBUG',		0x20);
define('LOG_LEVEL_ALL',			0x2F);

class Logger {
	
	private $level;
	private $appenders;
	private $starttime;
	
	public function __construct() {
		$this->level = 0;
		$this->appenders = array();
		$this->starttime = microtime(true);
	}
	
	public function setLevel($level) {
		$this->level = ($level * 2) - 1;
	}
	
	/**
	 * Get the name of a level
	 *
	 * @static
	 * @param int $level
	 */
	public function levelToString($level) {
		$levelMap = array(
			LOG_LEVEL_FATAL 		=> 'FATAL'
			,LOG_LEVEL_CRITICAL 	=> 'CRITICAL'		
			,LOG_LEVEL_ERROR 		=> 'ERROR'
			,LOG_LEVEL_WARNING 		=> 'WARNING'
			,LOG_LEVEL_INFO 		=> 'INFO'
			,LOG_LEVEL_DEBUG 		=> 'DEBUG');
			
		return $levelMap[$level];
	}
	
	public function setMask($mask) {
		$this->level = $mask;
	}
	
	public function getLevel() {
		return $this->level;	
	}
	
	public function log($msg, $level) {
		
		if ($level >= $this->level)
			return;
			
		// there are no appenders to log to
		if (! count($this->appenders))
			return;
			
		$event = new LogEvent($this, $msg, $level);
		
		for ($x = 0; $x < count($this->appenders); $x++) {
			$this->appenders[$x]->append($event);
		}
	}
	
	/**
	 * Add a output appender to this logger
	 *
	 * @param Appender $appender
	 */
	public function addAppender(&$appender) {
		$this->appenders[] =& $appender;
	}
	
	public function getStartTime() {
		return $this->starttime;
	}
	
	public function fatal($msg) {
		$this->log($msg, LOG_LEVEL_FATAL);
	}
	
	public function critical($msg) {
		$this->log($msg, LOG_LEVEL_CRITICAL);
	}
	
	public function error($msg) {
		$this->log($msg, LOG_LEVEL_ERROR);
	}
	
	public function warning($msg) {
		$this->log($msg, LOG_LEVEL_WARNING);
	}
	
	public function info($msg) {
		$this->log($msg, LOG_LEVEL_INFO);
	}
	
	public function debug($msg) {
		$this->log($msg, LOG_LEVEL_DEBUG);
	}	
		
}

interface Appender {
	
	/**
	 * Append a log event
	 *
	 * @abstract
	 * @param LogEvent $event
	 */
	public function append($event);
}

class PrintAppender implements Appender {
	
	/**
	 * @var LogLayout $layout
	 * @access private
	 */
	private $layout;
	
	public function __construct($layout = null) {
		if ($layout instanceof LogLayout) {
			$this->layout = $layout;
		} else {
			$this->layout = new SimpleLayout();
		}
	}
	
	/**
	 * Append a log event
	 *
	 * @param LogEvent $event
	 */
	public function append($event) {
		echo $this->layout->format($event) . "<br>";
	}
}

class StringAppender implements Appender {

	/**
	 * @var LogLayout $layout
	 * @access private
	 */
	private $layout;
	
	/**
	 * @var string $ouput
	 */
	private $output;
	
	function __construct($layout = null) {
		if ($layout instanceof LogLayout) {
			$this->layout = $layout;
		} else {
			$this->layout = new SimpleLayout();
		}
		
		$this->output = '';
	}
	
	/**
	 * Append a log event
	 *
	 * @param LogEvent $event
	 */
	public function append($event) {
		$this->output .= $this->layout->format($event);
	}
	
	public function getOutput() {
		return $this->output;
	}
}

interface LogLayout {
	
	/**
	 * Format a log event
	 *
	 * @abstract 
	 * @param LogEvent $event
	 */
	function format($event);
}

class SimpleLayout implements LogLayout{
	
	public function __construct() {
		
	}	
	
	/**
	 * Format a log event
	 *
	 * @param LogEvent $event
	 */
	public function format($event) {
		
		$indent = str_pad('', 18, ' ');
		$message = str_replace("\n", "\n" . $indent, $event->getMessage());
		
		return sprintf("%8.3f %- 8s %s\n"
			,$event->getStartTime()
			,Logger::levelToString($event->getLevel())
			,$message);
	}
}

class LogEvent {
	
	private $logger;
	private $message;
	private $level;
	private $starttime;
	
	/**
	 * Constructor, creates a new LogEvent instance
	 *
	 * @param Logger $logger
	 * @param string $message
	 * @param int $level
	 */
	public function __construct($logger, $message, $level) {
		$this->logger = $logger;
		$this->message = $message;
		$this->level = $level;
		$this->starttime = microtime(true);
	}
	
	public function getMessage() {
		return $this->message;
	}
	
	public function getLevel() {
		return $this->level;
	}
	
	public function getStartTime() {
		return ($this->starttime - $this->logger->getStartTime()) * 1000;	
	}
}
?>